monadtest is package{
  contract monad over %%c is {
    ret has type (%e)=>%%c of %e;
    bind has type (%%c of %e,(%e)=>%%c of %f) => %%c of %f;
    step has type (%%c of %e, %%c of %f) => %%c of %f;
    fail has type (string) => %%c of %a;
    
    step(M,N) default is bind(M, (function(_) is N));
  }
  
  implementation monad over cons is {
    ret(X) is cons of {X};
    bind(Ll,F) is let{
      apply(nil,A) is flat(A,nil);
      apply(cons(E,L), A) is apply(L,cons(F(E),A));
      
      private
      flat(nil,A) is A;
      flat(cons(E,L),A) is flat(L,concat(E,A));
      
      private concat(nil,A) is A;
      concat(cons(E,X),Y) is cons(E,concat(X,Y));
    } in apply(Ll,nil);
    
    fail(S) is nil;
  }
  
  main() do {
    X0 is cons of {"alpha"; "beta"};
    logMsg(info,"X0=$X0");
    X1 is bind(X0,(function(X) is cons of {X;X}));
    logMsg(info,"X1=$X1");
    assert X1=cons of {"alpha";"alpha";"beta";"beta"}
  }
  
}
    